#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
 
#include "data_type.h"
#include "cube.h"
#include "cube_define.h"
#include "cube_record.h"

#include "user_define.h"
#include "action_define.h"
#include "key_event.h"
// add para lib_include
int key_event_init(void * sub_proc, void * para)
{
	int ret;
	// add yorself's module init func here
	return 0;
}
int key_event_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	// add yorself's module exec func here
	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if(!memdb_find_recordtype(type,subtype))
		{
			printf("message format (%d %d) is not registered!\n",
			message_get_type(recv_msg),message_get_subtype(recv_msg));
			continue;
		}
		if((type==TYPE(USER_DEFINE))&&(subtype==SUBTYPE(USER_DEFINE,RETURN)))
		{
			ret=proc_keyevent_return(sub_proc,recv_msg);
		}
		else if((type==TYPE(GENERAL_RETURN))&&(subtype==SUBTYPE(GENERAL_RETURN,STRING)))
		{
			ret=proc_keyevent_general(sub_proc,recv_msg);
		}
	}
	return 0;
}

int proc_keyevent_general(void * sub_proc,void *recv_msg)
{
    RECORD(GENERAL_RETURN,STRING) * action_info;
    int ret;
	ret=message_get_record(recv_msg,&action_info,0);
	if(ret<0)
		return ret;

    if(Strcmp(action_info->name,"key_exchange")==0)
    {
		 ret=proc_keyevent_send(sub_proc,recv_msg);
    }   
    else if(Strcmp(action_info->name,"pcr_quote")==0)
    {
		 ret=proc_keyevent_quote(sub_proc,recv_msg);
    }
    return ret;
          
}
	
int proc_keyevent_return(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(USER_DEFINE,RETURN) * login_return;
	RECORD(MESSAGE,INSTANCE_INFO) * instance_info;
	char user_name[DIGEST_SIZE];
	void * new_msg;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	

	ret=message_get_record(recv_msg,&login_return,0);
	if(ret<0)
		return ret;

    if(login_return->return_code!=SUCCEED)
        return -EINVAL;

	instance_info=Talloc0(sizeof(*instance_info));
	if(instance_info==NULL)
		return -ENOMEM;
	
	proc_share_data_getvalue("proc_name",instance_info->proc_name);
	proc_share_data_getvalue("user_name",instance_info->user_name);
	//proc_share_data_getvalue("uuid",instance_info->node_uuid);

	new_msg=message_create(TYPE_PAIR(USER_DEFINE,RETURN),NULL);	
	if(new_msg==NULL)
		return -EINVAL;
	ret=message_add_record(new_msg,login_return);
	if(ret<0)
		return ret;

	ret=message_add_expand_data(new_msg,TYPE_PAIR(MESSAGE,INSTANCE_INFO),instance_info);
	if(ret<0)
		return ret;
    
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}

int proc_keyevent_quote(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(ACTION_DEFINE,HANDSHAKE) * action_info;
	RECORD(GENERAL_RETURN,STRING) * getkey_info;
	RECORD(MESSAGE,INSTANCE_INFO) * instance_info;
	RECORD(USER_DEFINE,CLIENT_STATE) * client_state;
	char user_name[DIGEST_SIZE];
	void * new_msg;
    MSG_EXPAND * msg_expand;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	
    // get shakehand define info from message
	ret=message_remove_expand(recv_msg,TYPE_PAIR(ACTION_DEFINE,HANDSHAKE),&msg_expand);
	if(ret<0)
		return ret;
    // no shakehand info means first step, use quote_req to generate shakehand info
    if(msg_expand==NULL)
    {
        printf("enter pcr_quote keyevent!\n");
        return proc_keyevent_quote_req(sub_proc,recv_msg);        
    }

    printf("msg_expand is %p!\n",msg_expand);
    action_info=msg_expand->expand;
    // if there is shakehand info, compate client_state->name and action_info's client/server 
    // to judge it is quote requestor or quote repoter  
	client_state = proc_share_data_getpointer();


    if(action_info->handshake_type==PCR_QUOTE)
    {
        
        if(Strcmp(client_state->user_name,action_info->server)==0)
        // now it is reporter
        {
            switch(action_info->step)
            {
                case 1:
                    printf("enter quote_report keyevent!\n");
                    message_remove_expand(recv_msg,TYPE_PAIR(MESSAGE,UUID_RECORD),&msg_expand);
                    action_info->step++;
                    message_add_expand_data(recv_msg,TYPE_PAIR(ACTION_DEFINE,HANDSHAKE),action_info);
                
                    ret=ex_module_sendmsg(sub_proc,recv_msg);
                    return ret;
                default:
                    return -EINVAL;
                        
            }
        }
        else if(Strcmp(client_state->user_name,action_info->client)==0)
        // now it is reporter
        {
            switch(action_info->step)
            {
                // if we need to do sth for quote req back data
                case 1:

                    printf("enter quote_req first keyevent!\n");
                    ret=ex_module_sendmsg(sub_proc,recv_msg);
                    return ret;
                case 2:
                    printf("enter quote_verify keyevent!\n");
                    message_remove_expand(recv_msg,TYPE_PAIR(MESSAGE,UUID_RECORD),&msg_expand);
                    action_info->step++;
                    message_add_expand_data(recv_msg,TYPE_PAIR(ACTION_DEFINE,HANDSHAKE),action_info);
                    ret=ex_module_sendmsg(sub_proc,recv_msg);
                    return ret;
                    

                default:
                    return -EINVAL;
                        
            }
        }
    }

    return -EINVAL;
}

int proc_keyevent_key_exchange(void * sub_proc,void * recv_msg)
{
	RECORD(ACTION_DEFINE,HANDSHAKE) * action_info;
	RECORD(GENERAL_RETURN,STRING) * remotekey_info;
	char user_name[DIGEST_SIZE];
	void * new_msg;
	int ret;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	

	ret=message_get_record(recv_msg,&action_info,0);
	if(ret<0)
		return ret;

	remotekey_info=Talloc0(sizeof(*remotekey_info));
	if(remotekey_info==NULL)
		return -ENOMEM;
    remotekey_info->return_value=dup_str(action_info->server,DIGEST_SIZE);
    remotekey_info->name=dup_str("key_exchange",0);

	new_msg=message_create(TYPE_PAIR(GENERAL_RETURN,STRING),recv_msg);	
//	ret=message_add_expand_data(recv_msg,TYPE_PAIR(MESSAGE,INSTANCE_INFO),instance_info);	
	if(ret<0)
		return ret;
	ret=message_add_record(new_msg,remotekey_info);
	if(ret<0)
		return ret;	
	ret=message_add_expand_data(recv_msg,TYPE_PAIR(ACTION_DEFINE,HANDSHAKE),action_info);	
	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}

int proc_keyevent_send(void * sub_proc,void * recv_msg)
{
	int ret;
	RECORD(GENERAL_RETURN,STRING) * login_sendkey;
	RECORD(MESSAGE,INSTANCE_INFO) * instance_info;
	char user_name[DIGEST_SIZE];
	void * new_msg;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	

	ret=message_get_record(recv_msg,&login_sendkey,0);
	if(ret<0)
		return ret;

	instance_info=Talloc0(sizeof(*instance_info));
	if(instance_info==NULL)
		return -ENOMEM;
	
	proc_share_data_getvalue("proc_name",instance_info->proc_name);
	Strncpy(instance_info->user_name,login_sendkey->return_value,DIGEST_SIZE);
	proc_share_data_getvalue("uuid",instance_info->node_uuid);

	new_msg=message_create(TYPE_PAIR(MESSAGE,INSTANCE_INFO),recv_msg);	
//	ret=message_add_expand_data(recv_msg,TYPE_PAIR(MESSAGE,INSTANCE_INFO),instance_info);	
//	if(ret<0)
//		return ret;
	ret=message_add_record(new_msg,instance_info);
	if(ret<0)
		return ret;	
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}

int proc_keyevent_quote_req(void * sub_proc,void * recv_msg)
{
	RECORD(ACTION_DEFINE,HANDSHAKE) * action_info;
	RECORD(GENERAL_RETURN,INT) * pcr_info;
	RECORD(GENERAL_RETURN,STRING) * sys_info;
	RECORD(USER_DEFINE,CLIENT_STATE) * client_state;
	char user_name[DIGEST_SIZE];
	void * new_msg;
	int ret;
    int offset=0;
	
	BYTE local_uuid[DIGEST_SIZE];	
	char proc_name[DIGEST_SIZE];	
    BYTE Buffer[DIGEST_SIZE];

    // get pcr quote command , name is pcr_quote, return_value is user_name:pcrindex(string)
	ret=message_get_record(recv_msg,&sys_info,0);
	if(ret<0)
		return ret;
    //  prepare to add shakehand info, client is quote requestor&verifier, server is quote reporter
    //  handshake_type is PCR_QUOTE, step starts at 1
    action_info=Talloc0(sizeof(*action_info));
    if(action_info==NULL)
        return -ENOMEM;

	client_state = proc_share_data_getpointer();
    RAND_bytes(client_state->nonce,DIGEST_SIZE);
    Memcpy(action_info->nonce,client_state->nonce,DIGEST_SIZE);
    action_info->client=dup_str(client_state->user_name,DIGEST_SIZE);
    action_info->step=1;
    action_info->handshake_type=PCR_QUOTE;
    offset=Getfiledfromstr(Buffer,sys_info->return_value,':',DIGEST_SIZE);
    if(offset<=0)
        return -EINVAL;
    action_info->server=dup_str(Buffer,DIGEST_SIZE);

    // add pcr index in msg_expand (GENERAL_RETURN,INT), name is pcr_select, return_value is pcrindex
	pcr_info=Talloc0(sizeof(*pcr_info));
	if(pcr_info==NULL)
		return -ENOMEM;

    pcr_info->name=dup_str("pcr_select",DIGEST_SIZE);
    pcr_info->return_value=Atoi(sys_info->return_value+offset+1,DIGEST_SIZE);
    
	ret=message_add_expand_data(recv_msg,TYPE_PAIR(ACTION_DEFINE,HANDSHAKE),action_info);	
	if(ret<0)
		return ret;	
	ret=message_add_expand_data(recv_msg,TYPE_PAIR(GENERAL_RETURN,INT),pcr_info);	
	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,recv_msg);
	return ret;
}
